맨위로가기

예외 처리

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

예외 처리는 서브루틴의 전제 조건 위반 시 발생하는 예외를 감지하고 처리하는 메커니즘이다. 프로그래머가 "정상"으로 간주하는 상황에 따라 예외의 정의가 달라지며, 유효하지 않은 인수, 사용 불가능한 리소스, 특별한 처리가 필요한 정상적인 조건 등이 일반적인 예외로 발생할 수 있다. 예외 처리는 반환 코드와 같은 다른 방식으로 오류를 알리는 C와 같은 언어의 반 전제 문제를 해결하며, 예외는 오류보다 더 넓은 의미를 가진다. 최초의 하드웨어 예외 처리는 1951년 유니박 I에서 발견되었고, 소프트웨어 예외 처리는 1960년대와 1970년대에 개발되었으며, 1980년대 이후 많은 프로그래밍 언어에서 널리 채택되었다. 예외는 컴퓨터 하드웨어 문제, 운영 체제 설정 실수, 사용자의 입력 실수 등으로 발생할 수 있으며, 예외 안전성은 코드의 런타임 실패가 악영향을 미치지 않는 정도를 의미한다. C++, Java, JavaScript 등 많은 프로그래밍 언어에서 예외 처리를 지원하며, IEEE 754 부동 소수점 표준은 예외적인 상황에 대한 예외 처리 루틴을 호출하는 "트래핑"을 지원한다. 예외 처리는 시스템 장애를 방지하는 데 중요하며, 예외는 시스템 담당자가 해결해야 하는 상황을 의미하고, 예외 처리 과정에서 제어 흐름을 사용하는 것은 안티 패턴으로 간주될 수 있다.

더 읽어볼만한 페이지

  • 소프트웨어 이상 - NaN
    NaN(Not a Number)은 IEEE 754 표준에서 숫자가 아님을 나타내는 특수한 값으로, 지수부가 모두 1이고 가수부가 0이 아닌 비트 패턴으로 표현되며, 연산 시 예외를 발생시키지 않고 전파되는 quiet NaN과 예외를 발생시키는 signaling NaN이 있다.
  • 소프트웨어 이상 - 충돌 (컴퓨팅)
    충돌은 프로그램 설계 결함, 운영체제 결함, CPU/GPU 과열, 메모리 오류 등으로 인해 소프트웨어가 비정상적으로 종료되거나 작동이 멈추는 현상으로, 데이터 손실, 시스템 불안정 등의 영향을 초래하여 자동 복구, 운영을 통한 완화, 충돌 보고 등의 기술이 사용된다.
  • 제어 흐름 - 프로그램 카운터
    프로그램 카운터는 CPU 내에서 다음에 실행될 명령어의 주소를 저장하는 레지스터로, 명령어 사이클의 fetch 단계에서 사용되어 명령어를 가져오고 실행 후 갱신되며, CPU 성능 향상 기술과 현대 프로그래밍 모델에 영향을 미친다.
  • 제어 흐름 - 브랜치 (컴퓨터 과학)
    프로그램 실행 흐름을 제어하는 명령어인 브랜치는 점프, 호출, 반환 명령어로 나뉘며, CPU 플래그 레지스터와 주소 지정 방식을 활용하여 코드 분기 및 서브루틴 호출/반환을 수행하지만, 파이프라인 CPU에서 성능 저하를 유발하여 분기 예측 등의 기술로 해결합니다.
  • 프로그래밍 구성체 - 형 변환
    형 변환은 프로그래밍에서 변수의 데이터 타입을 변경하는 것으로, 암시적 형 변환과 명시적 형 변환으로 나뉘며, 객체 지향 프로그래밍에서는 업캐스팅과 다운캐스팅이 발생하고, 각 언어는 고유한 규칙과 방법을 제공하며 잘못된 형 변환은 오류를 유발할 수 있다.
  • 프로그래밍 구성체 - 연산자 오버로딩
    연산자 오버로딩은 프로그래밍 언어에서 기존 연산자를 사용자 정의 자료형에 대해 재정의하여 내장 자료형처럼 다루도록 하는 기능으로, 코드 가독성과 표현력을 높이지만 남용 시 코드 의미를 모호하게 만들 수 있다.
예외 처리

2. 정의

예외는 각 서브루틴이 "정상적으로" 종료되기 위한 전제 조건이 위반되었을 때 발생한다.[1] 이는 정상적인 실행 흐름에서 벗어나는 상황을 의미한다. 예외 처리 메커니즘은 이러한 전제 조건 위반 시 예외를 발생시키고 처리한다.

예외의 정의는 주관성을 띄며, 프로그래머가 어떤 상황을 "정상"으로 간주하는지에 따라 달라진다.[1] 예를 들어, 프로그래머는 0으로 나누는 것을 예외로 처리하거나, 0 또는 특수 값을 반환하도록 설계할 수 있다. 일반적인 예외로는 유효하지 않은 인수(예: 값이 함수의 영역 밖에 있는 경우),[2] 사용 불가능한 리소스(예: 파일 누락,[3] 네트워크 드라이브 오류,[4] 메모리 부족 오류[5]), 또는 루틴이 특별한 처리가 필요한 정상적인 조건(예: 주의, 파일 끝[6]) 등이 있다. 사회적 압력은 예외의 범위와 예외 처리 메커니즘 사용에 큰 영향을 미친다.[7]

예외 처리는 정상 반환 값과 오류 값을 구별하여 반 전제 문제를 해결한다. C와 같이 예외 처리가 내장되지 않은 언어는 반환 코드 및 errno 패턴과 같은 다른 방식으로 오류를 알린다.[8] "예외"라는 용어는 "오류"보다 더 넓은 의미를 가지며, 반드시 문제가 있음을 의미하지는 않는다.[10] 한 프로그래머에게 오류로 간주되는 조건이 다른 프로그래머에게는 정상적인 상황일 수 있다.

"예외"라는 용어는 예외 발생이 드물다는 의미를 내포하여 오해를 일으킬 수 있지만,[9] 실제로는 프로그램에서 정상적인 상황일 수 있다.[10] 예를 들어, 연관 배열 조회 시 키가 없으면 예외가 발생할 수 있으며, 이는 성공적인 조회보다 더 자주 발생할 수 있다.

3. 역사

최초의 하드웨어 예외 처리는 1951년 유니박 I에서 발견되었다. 산술 오버플로는 주소 0에서 두 개의 명령을 실행하여 제어를 전송하거나 결과를 수정할 수 있었다.[11][37] 소프트웨어 예외 처리는 1960년대와 1970년대에 개발되었다. 예외 처리는 1980년대 이후 많은 프로그래밍 언어에서 널리 채택되었다.

4. 예외의 원인

예외는 다음 원인으로 발생할 수 있다.



하드웨어 관련 예외는 구현 관점에서 인터럽트와 동일하게 처리된다.[12] 프로세서는 현재 프로그램 실행을 중단하고, 인터럽트 벡터 테이블에서 해당 예외 또는 인터럽트 조건에 대한 인터럽트 핸들러를 찾아 상태를 저장하고 제어를 전환한다.[12]

5. 예외 안전

메모리 누수, 잘못된 출력, 왜곡된 자료와 같이 코드의 런타임 실패가 악영향을 미치지 않는다면, 이러한 코드를 예외 안전이라고 부른다.[22][23] 예외 안전한 코드는 예외가 발생하더라도 코드에 내재된 불변 조건을 만족시켜야 한다. 예외 안전성은 몇 가지 수준으로 나뉜다[22][23]:


  • 미전파 보장 또는 실패 투명성: 연산은 성공이 보장되며, 예외적인 상황에서도 모든 요구 사항을 충족한다. 예외가 발생하더라도 해당 예외를 상위로 전파하지 않는다. (최고 수준의 예외 안전성)
  • 강력한 예외 안전성, 커밋-또는-롤백 시맨틱스[24] 또는 무변경 보장: 연산이 실패할 수 있지만, 실패한 연산은 부작용을 일으키지 않으며 모든 데이터는 원래 값을 유지한다.
  • 기본적 예외 안전성: 실패한 연산의 불완전한 실행으로 인해 부작용이 발생할 수 있지만, 상태의 불변 조건은 유지된다. 모든 저장된 데이터는 더 이상 실행 전과 다르더라도 유효한 값을 갖는다.
  • 예외 안전성 없음: 아무것도 보장되지 않는다. (최저 수준의 예외 안전성)

6. 프로그래밍 언어에서의 예외 지원

C++, 에이다, 자바, 자바스크립트와 같은 프로그래밍 언어는 예외 처리 기능을 포함하고 있다. 이러한 언어에서는 예외 처리를 자동화하고 있다. 예외 처리를 담당하는 핸들러를 찾아 순서대로 콜 스택을 거슬러 올라가 올바른 핸들러를 찾아내면 그 곳에 처리를 맡긴다.

일부 프로그래밍 언어는 내장된 예외 처리 기능을 제공한다. 예를 들어 Ada, C++, Java, Scala, C#, JavaScript, OCaml 등이 있다. 이러한 언어에서는 전용 언어 기능을 통해 프로그래머가 예외 처리를 기술하는 수고를 줄이고 있다.

예외가 발생한 것을 간과하고 정상적인 동작을 계속하면, 더욱 심각하고 치명적인 이상을 초래할 수 있다. 이를 피하려면 예외 발생 여부를 면밀히 확인하고, 예외가 감지된 경우에는 적절한 사후 처리를 해야 한다. 그러나, 대규모 프로그램에서는 이러한 확인 작업이 방대해지고, 원래 목적인 정상적인 처리보다 더 많은 기술이 필요할 수도 있다.

따라서, 이러한 언어에서는 예외 발생 확인을 거의 자동화하고 있다. 예외가 발생하면 현재 처리를 중단한다. 발생한 예외의 사후 처리를 담당할 수 있는 핸들러를 찾아 차례로 콜 스택 (함수 호출)을 거슬러 올라가, 적절한 핸들러를 찾으면 그에 사후 처리를 맡긴다. 이로 인해 거슬러 올라가는 도중에 이 예외를 처리할 능력이 없는 처리는 자동으로 중단된다.

Scheme에서는 언어 수준의 예외 처리를 지원하지 않지만, 이는 지속성이 존재하기 때문에 예외를 라이브러리 수준에서 실현할 수 있기 때문이다 (표준 사양인 SRFI-34에서 정의되어 있다).

언어 기능으로는 예외 처리 구문이 존재하지 않지만, 별도로 예외 처리를 갖춘 언어도 존재한다. 스몰토크는 언어 기능으로 제어 구문이 거의 없다. 이 때문에 예외 처리 구문도 블록을 조합한 메시지식으로 기술하도록 되어 있다. 언어 기능이 아니기 때문에 매우 유연성이 있어 블록의 반환값을 변경하거나 예외가 발생한 식의 중간부터 복귀하는 등 다양한 제어가 가능하게 되어 있다.

6. 1. C++ 에서의 예외 처리

C++에이다, 자바, 자바스크립트와 같은 프로그래밍 언어처럼 예외 처리 기능을 포함하고 있다. 이러한 언어에서는 예외 처리를 자동화하고 있으며, 예외 처리를 담당하는 핸들러를 찾아 순서대로 콜 스택을 거슬러 올라가 올바른 핸들러를 찾아내면 그 곳에 처리를 맡긴다.

C++에서는 `try-catch` 블록을 사용하여 예외를 처리한다. `throw` 키워드를 사용하여 예외를 발생시키며, `catch` 블록은 예외의 타입에 따라 여러 개를 정의할 수 있다. 모든 예외를 처리하는 `catch(...)` 블록을 사용할 수도 있다.

```cpp

void Function1(void)

{

try

{

Function0();

}

catch( int exception ) // (3)

{

// 복구 처리

}

catch( char const *exception ) // (4)

{

// 복구 처리

}

catch( std::exception const &exception ) // (5)

{

// 복구 처리

}

catch(...) // (6)

{

// 복구 처리

throw; // (7)

}

}

```

`try` 블록 내에서 호출된 함수 `Function0()`가 `throw`를 실행하면, `Function1()`의 `catch` 문으로 제어가 이동한다. C++에서는 `std::exception` 혹은 그 파생형 이외의 타입 값도 `throw`로 던질 수 있으며, 타입에 대응하는 `catch` 문에서 포착할 수 있다.[25]

C++의 특징적인 구문으로, 모든 예외를 포착할 수 있는 `catch(...)`가 존재한다. 이는 다른 `catch`가 놓친 예외라도 잡아야 할 경우에 사용된다. 값을 지정하지 않는 `throw`를 잡을 수 있는 것도 `catch(...)`뿐이다.[25] `catch` 문 안에서 인수가 없는 `throw`를 사용하면 예외를 재전송한다.[25]

`try` 블록은 함수 전체에 적용할 수도 있는데 이를 function-try-block이라고 한다. `catch` 문에서는 지역 변수를 참조할 수 없고 인수만 참조할 수 있지만, 생성자의 초기화 목록에서 발생한 예외나 소멸자[26]에서 던져진 예외는 이 방법으로만 포착할 수 있다.

C++의 예외 처리 구문은 Java나 JavaScript, C# 등 많은 후발 언어의 규범이 되었다.

C++의 '''예외 지정'''은 함수에서 전달되는 예외의 종류를 명시하는 기능이다.[27] 예를 들어 `void f() throw(int)`는 int형의 에러를 throw할 수 있음을 명시한다. 하지만, C++11에서 비권장되었으며, C++17에서 폐지되었다.[28]

6. 2. Java 에서의 예외 처리

C++, 에이다, 자바, 자바스크립트와 같은 프로그래밍 언어는 예외 처리 기능을 포함하고 있다. 이러한 언어에서는 예외 처리를 자동화하고 있다. 예외 처리를 담당하는 핸들러를 찾아 순서대로 콜 스택을 거슬러 올라가 올바른 핸들러를 찾아내면 그 곳에 처리를 맡긴다.

Java에서는 예외를 클래스로 구현한다. 예외를 "던지는(throw)" 메서드는 `throws Exception`과 같이 지정한다. Java 플랫폼상에서 자바 언어를 사용하고, 발생하는 예외가 `java.lang.Exception`을 상속하지만, `java.lang.RuntimeExcption`을 상속하지 않는 경우, `try`/`catch` 문으로 예외 처리를 명시적으로 기술하거나, 메서드에 `throws`를 추가해야 한다. 다만, Java 플랫폼에서 동작하는 언어 중 Groovy나 Scala 등 자바 언어 이외의 많은 언어는 `RuntimeException` 이외의 예외에 대해 반드시 명시적으로 기술하지 않아도 되도록 되어 있다.

`try-catch-finally` 블록을 사용하여 예외를 처리하고,`throws` 키워드를 사용하여 메서드가 발생시킬 수 있는 예외를 명시하며,`Exception` 클래스를 상속받아 사용자 정의 예외를 만들 수 있다.

```java

public void throwError() throws Exception {

throw new Exception();

}

public void catchException() {

try {

throwError();

} catch (Exception e) {

e.printStackTrace();

}

}

6. 3. Python 에서의 예외 처리

C++, 에이다, 자바, 자바스크립트와 같은 프로그래밍 언어는 예외 처리 기능을 포함하고 있다. 이러한 언어에서는 예외 처리를 자동화하고 있다. 예외 처리를 담당하는 핸들러를 찾아 순서대로 콜 스택을 거슬러 올라가 올바른 핸들러를 찾아내면 그 곳에 처리를 맡긴다.

예외를 눈치채지 못하다가 해당 문제를 실제 동작으로 넘길 경우, 치명적인 문제를 일으킬 수 있다. 이를 막으려면 예외가 일어난 부분을 철저하게 검사해야 한다. 다만 프로그램의 크기가 크다면 일반적인 예외 처리보다 더 많은 기술을 요구할 수도 있다.

파이썬에서는 `try-except-finally` 블록을 사용하여 내장 예외와 사용자 정의 예외를 모두 처리할 수 있다.

7. IEEE 754 부동 소수점 예외

IEEE 754 부동 소수점 표준의 예외 처리는 일반적으로 예외적인 상황을 의미하며, 예외를 "특정 피연산자에 대한 연산이 모든 합리적인 응용 프로그램에 적합한 결과를 갖지 못하는 경우 발생하는 이벤트"로 정의한다.[13] 해당 연산은 기본 동작을 호출하거나, 명시적으로 요청된 경우 언어 정의 대체 처리를 호출하여 하나 이상의 예외를 알릴 수 있다.

기본적으로 IEEE 754 예외는 재개 가능하며, 다양한 예외에 대해 미리 정의된 값을 대체하여 처리한다. 예를 들어 0으로 나누기 예외의 경우 무한대를 사용하고, 예외 발생 여부를 나중에 확인할 수 있도록 상태 플래그를 제공한다.[13] 상태 플래그를 사용하는 예외 처리 스타일은 먼저 빠르고 직접적인 구현을 사용하여 식을 계산하고, 상태 플래그를 테스트하여 실패 여부를 확인한 다음, 필요한 경우 더 느리고 수치적으로 더 강력한 구현을 호출하는 방식이다.

IEEE 754 표준은 예외적인 상황에서 사용자가 제공한 예외 처리 루틴을 호출하는 것을 "트래핑"이라고 부르며, 이는 표준의 선택적 기능이다. 이 표준은 값의 기본 사전 대체를 따르는 재개, 제거 가능한 특이점을 간결하게 처리하는 것을 포함하여 여러 가지 사용 시나리오를 권장한다.[13][14][15]

기본적인 IEEE 754 예외 처리 동작은 기본값을 사전 대체한 후 재개하는 방식으로, 수치적 예외 발생 시 프로그램 제어 흐름을 변경하는 데 따른 위험을 피한다. 예를 들어, 1996년 클러스터 우주선 발사는 산술 오류 발생 시 계산을 중단하는 에이다의 예외 처리 정책으로 인해 부분적으로 치명적인 폭발로 끝났다. 윌리엄 카한은 기본 IEEE 754 예외 처리 동작이 이를 방지했을 것이라고 주장한다.[14]

8. 사용자 인터페이스에서의 예외 처리

프론트엔드 웹 개발 프레임워크인 React 및 Vue와 같은 프레임워크는 사용자 인터페이스 (UI) 컴포넌트 계층 구조로 오류가 전파되는 오류 처리 메커니즘을 도입했는데, 이는 코드 실행 시 오류가 호출 스택으로 전파되는 방식과 유사하다.[16][17] 여기서 오류 경계 메커니즘은 일반적인 try-catch 메커니즘과 유사하게 작동한다. 따라서 컴포넌트는 하위 컴포넌트의 오류를 캡처하고 처리하여 상위 컴포넌트로 전파되지 않도록 할 수 있다.

9. 예외 처리의 동작과 중요성

예외 처리는 시스템을 구성하는 프로그램의 각 호출 계층에서 호출 대상이 예상하지 못한 입력값을 받아 문제가 발생했을 경우, 문제에 맞는 예외를 발생시켜 호출 측에 처리를 반환하는 방식으로 동작한다. 호출 측에 예외를 반환함으로써 호출 측에서 문제 해결이 이루어지기를 기대하지만, 어떤 호출 계층의 예외 처리에서도 문제 해결을 할 수 없는 경우, 시스템의 내부 상태에 모순이 남아 시스템 장애가 발생한다. 예외 발생 후, 시스템이 동작하고 있더라도, 예외에 대한 대처 결과가 설계에서 벗어난 경우, 시스템의 내부 상태에 모순이 생겨 스토리지나 데이터베이스 또는 네트워크에 무의미한 데이터를 출력할 가능성이 생긴다. 이는 데이터 손상뿐만 아니라, 연계된 다른 시스템에도 모순이 전파되어 광범위한 시스템 장애로 이어질 수 있다. 따라서, 예외 처리는 시스템 장애를 미연에 방지한다는 의미에서 매우 중요하다.

10. 예외와 오류의 차이

예외는 시스템 담당자가 문제 해결을 해야 하는 상황이다.[19][20][21] 예외 문제 해결 방법에는 예외를 무시하는 것도 포함되지만, 명확한 근거를 가지고 무시해야 한다. 예를 들어 설계의 일부로 일부 예외를 무시해도 문제 없다고 판단하거나, 연동하는 다른 시스템의 유지보수 중 예외 발생이 불가피한 경우 유지보수 완료를 기다리는 경우가 있다. 예외에 대해 사용자가 해결해야 할 문제를 오류라고 한다. 단, 업무 시스템 개발에서는 오류를 업무 오류, 예외를 시스템 오류라고 표현하는 경우도 있으며, 기술자 간의 엄격하게 통일된 견해는 존재하지 않는다. 어떤 시스템 개발을 하는 회사에서는 예외에 대한 대처가 부적절하면 사용자에게 손해를 입힐 수 있으므로, 시스템 장애를 발생시키는 예외 발생은 제품의 하자로 취급해야 한다.

11. 제어 흐름으로의 전용(轉用)

예외 처리 과정에서는 처리 흐름이 일반적인 제어와 크게 달라지는데, 이를 (오류 처리 이외의 목적으로 정상적인 흐름에서) 적극적으로 이용하는 것은 안티 패턴으로 여겨지기도 한다. 루비파이썬에서는 이터레이터가 종단에 도달하는, 무한 루프가 아니라면 반드시 발생하는 현상으로 인해 예외가 발생할 수도 있으며, 루비에서는 예외 처리와 관계없이 전역 탈출을 수행하는 제어 구조도 마련되어 있다.

스몰토크에서도 예외는 오류 처리 이외의 알림으로 사용된다. 스몰토크의 예외(Exception)는 오류(Error)와 알림(Notification)으로 구성되며, EOF의 알림이나 스레드 간의 인터럽트 종료 알림 등에 사용된다. 또한 스몰토크는 값을 검색한 결과 값이 발견되지 않은 경우 반환값으로 `nil`(null)을 반환하지 않는 경향이 있으며, 값이 발견되지 않으면 예외를 발생시킨다. 하지만 단순한 예외 처리라는 패턴이 있어, 값이 발견되지 않는 경우에도 예외 구문을 사용하지 않고 안전하게 복구할 수 있는 수단을 제공한다.

알림 예외에는 오류 예외와 다른 특징적인 점이 있다. 알림을 발생시킨 경우에는 오류와 달리 포착하지 않아도 처리가 중단되지 않고 그대로 속행된다.

12. 반환값과 예외

C 언어와 같이 예외 처리를 지원하지 않는 언어에서는 전통적으로 함수(서브루틴)의 반환값을 통해 함수의 성공 여부를 판단하는 방법을 사용해 왔다.[32][33] 관례적으로 함수의 반환값을 32비트 정수값 등으로 선언하고, 함수가 성공했을 경우 0을 반환하고, 실패했을 경우에는 에러 코드로 음수를 반환하는 경우가 많다. 성공/실패 결과를 부울값 1/0으로 반환하기도 한다. 반환값이 포인터형인 경우에는, 성공했을 경우 유효한 포인터(NULL이 아닌 값)를 반환하고, 실패했을 경우 무효한 포인터(NULL)를 반환하는 것이 일반적이다. 표준 라이브러리나 API에서는 상세한 에러 코드를 `errno`와 같은 전역 변수에 저장하기도 한다.[32][33]

반환값을 통한 처리 성공/실패 판정에는 다음과 같은 문제점이 있다.


  • 반환값은 무시될 수 있어, 호출 측에서 에러가 발생해도 정상 처리를 계속하는 프로그램 작성이 가능하다.
  • 에러 코드는 32비트 정수 값에 불과하므로, 상세 정보(예: 구체적인 원인, 이상 발생 위치 등을 나타내는 에러 메시지)를 추가할 수 없다.
  • 직전의 에러 정보를 전역 변수에 저장하는 설계는 멀티 스레드 대응 시 별도로 스레드 로컬 스토리지화가 필요하다.
  • 반환값을 매번 체크하는 판정문을 작성하는 것이 번거롭다.
  • 반환값에 정상적인 값과 이상적인 값(에러 판정용 값)을 혼재시키거나, 또는 정상적인 값과 이상적인 값으로 반환값을 구별할 수 없는 함수는, 함수 호출 결과의 반환값을 안에서 그대로 사용할 수 없게 된다.


이러한 문제점 때문에, 반환값을 정상적인 결과 획득에 사용할 수 없어 인수를 처리 결과 획득용으로 사용하여 함수 인터페이스 및 호출 측 코드가 복잡해지는 문제가 발생한다. 또한, 함수의 호출 결과를 일단 지역 변수에 저장하지 않고 다음 함수 인수로 그대로 식으로 전달하는 것도 불가능해진다.

주 기억 영역의 용량이나 파일 용량을 얻는 함수를 설계할 때, 결과를 부호 없는 정수형(비음수)의 반환값으로 반환하도록 결정한 경우, 반환값으로 에러 판정용 값을 반환할 수 없다. 이 경우, `errno`와 같은 전역 변수 또는 별도로 준비한 함수 인수를 통해 에러 코드를 반환하고, 호출 측에서 판정해야 한다. Windows API의 `GetFileSize()` 함수는 반환값으로 파일 크기를 반환하지만, 에러가 발생한 경우 -1(실제로는 `0xFFFFFFFF`)을 반환하는 혼합된 설계를 가지고 있다. 이 값은 정상적인 값으로 있을 수 있는 값이므로, 에러 여부를 판정하려면 `GetLastError()` 함수의 호출이 별도로 필요하다.[34] `GetFileSizeEx()` 함수는 성공/실패를 반환값으로, 정상적인 결과 출력을 인수로 반환하는 설계로 되어 있으며, `GetFileSize()` 함수의 대체로 권장된다.[35]

C 언어에서 `malloc`, `calloc`, `realloc` 함수는 확보를 요구하는 용량으로 0을 지정했을 때 C 언어 규격으로 NULL을 반환해도 좋다고 정의되어 있다.[36] 이 때문에 이들 함수의 반환값으로는 기억 공간의 용량 부족인지, 인수에 0을 지정했는지 판단할 수 없어 분리하기 위해 `errno`를 조사하거나 인수를 조사해야 한다. C++에서 동일한 new 연산자는 이 점을 용량 부족일 때만 예외를 던지는 것으로 개선하고 있다.

언어 레벨에서의 예외 처리는 이러한 결점을 해소하고, 예외를 확실하게, 그리고 통일적으로 처리할 목적으로 도입된 것이라고 할 수 있다.

13. 복구 처리

예외 발생 시 적절한 복구 처리를 통해 프로그램의 실행을 계속하거나, 안전하게 종료할 수 있다. 처리 중에 사용 가능한 기억 영역이 고갈된 경우의 복구는 비교적 간단하다.

대량 데이터를 읽으려 할 때, 시스템 전체의 단기적인 기억 영역 고갈, 시스템 전체의 장기적인 기억 영역 고갈등 기억 영역이 원인이 될수 있다. GUI 프로그램의 이벤트 처리라면 장기적인 기억 영역 고갈 이외의 상황에서 복구할 수 있다. 단기적, 혹은 시스템 전체의 기억영역 고갈의 경우, 현재 실행 중인 처리를 중단하고 처리에 사용된 기억 영역을 해제하면 프로세스를 계속할 수 있을 가능성이 있다. 특히 시스템의 기억 영역 사용 상황이 회복되기를 기다려 다시 같은 처리를 실행할 수도 있다.

CUI의 경우, 여러 파일을 처리하는 상황에서 단기적인 기억 영역 고갈 상황에서 복구할 수 있다. 고갈이 발생한 대용량 파일의 처리는 불가능하더라도 그 이후의 소용량 파일은 처리할 수 있을 가능성이 있다.

다음은 스몰토크를 이용한 복구 처리의 예이다.

```smalltalk

| window textBox filePath open notifyArea |

"파일 읽기 실패를 알리는 부품"

notifyArea := LabelStringMorph new.

"파일을 열기 위한 부품"

textBox := TextMorph new.

filePath := TextMorph new.

open := SimpleButtonMorph new.

"버튼을 클릭하면 파일을 읽어 텍스트 상자에 표시할 수 있도록 한다"

open

on: #click

send: #value

to:

[

[

filePath contents asFile withReadStreamDo:

[ :readStream |

textBox contents: readStream contents.

]

]

"

기억 영역이 고갈된 경우의 복구 처리.

처리를 중단한 후, 가능한 자원을 해제한 다음 사용자에게 기억 영역 해제를 촉구한다.

예외가 발생한 후 예외를 포착하기까지 어느 정도의 기억 영역이 해제된다.

해제 후 여유가 있으면 복구할 수 있지만, 여유가 없는 경우에는 복구할 수 없는 경우도 있다.

"

on: AllocationFailure

do:

[

ObjectMemory compact. "가비지 컬렉터에 의한 해제"

textBox contents: ''. "중간까지 읽어들인 문자열을 해제"

ObjectMemory compact. "textBox contents 분의 해제"

notifyArea text: '기억 영역이 고갈되어 파일을 열 수 없습니다. 다른 프로그램을 종료한 후 다시 실행하십시오.'

].

].

"파일을 표시하는 텍스트 상자와 파일 읽기에 필요한 각종 부품을 결합한 Window를 표시한다"

window := SystemWindow new.

window

addMorph: textBox;

addMorph: filePath;

addMorph: open;

addMorph: notifyArea;

openInWorld.

14. 예외의 네스팅(중첩)

예외 처리 중에 원래 예외와 다른 예외가 발생하는 것을 예외의 네스팅(중첩)이라고 한다. 예외 네스팅 처리는 예외 처리 설계 및 구현을 복잡하게 만들기 때문에, 허용 범위 및 예외를 허용하지 않을 처리에 대한 고려가 필요하다.

언어 사양으로 구현된 예외는 네스팅 레벨이 제한되지 않는 경우가 일반적이지만, 무한 루프의 가능성이 있어 설계 및 구현 시 주의가 필요하다.

OS나 CPU 예외는 네스팅 레벨에 제한을 두는 경우가 많다. 예를 들어, 사용자 프로세스의 페이지 폴트는 허용하지만, 커널의 페이지 폴트는 허용하지 않아 예외 처리를 간소화한다. CPU에서는 예외 발생 후 핸들러 실행 중 스택 오버플로우 등으로 인해 더블 폴트(더블 폴트/Double fault영어)가 발생할 수 있으며, 이는 별도 핸들러 및 스택 등 복잡한 처리가 필요하다. 더블 폴트 처리 중 추가 예외 발생 시 트리플 폴트(트리플 폴트/Triple fault영어)가 되어 처리가 어려워 리셋하는 경우가 많다.

참조

[1] 간행물 Exception Handling and Software Fault Tolerance 1980
[2] java ArrayIndexOutOfBoundsException 2001
[3] java FileNotFoundException 2001
[4] 웹사이트 Unusual error message : java.io.SyncFailedException: sync failed https://groups.googl[...] 2023-11-17
[5] 웹사이트 Understand the OutOfMemoryError Exception https://docs.oracle.[...] 2023-11-17
[6] java FileNotFoundException
[7] 서적 Advanced Topics in Exception Handling Techniques http://staffwww.dcs.[...]
[8] 간행물 A study of the applicability of existing exception-handling techniques to component-based real-time software technology 1998-03
[9] 웹사이트 Thesaurus results for EXCEPTION https://www.merriam-[...] 2023-11-17
[10] 간행물 Exception Handling in CLU http://csg.csail.mit[...] 2021-12-19
[11] 웹사이트 Interrupts https://people.cs.cl[...] 2022-01-04
[12] 웹사이트 Art of Assembly: Chapter Seventeen https://www.plantati[...] 2021-12-22
[13] 간행물 Faster Numerical Algorithms via Exception Handling, IEEE Transactions on Computers, 43(8)
[14] 웹사이트 A Demonstration of Presubstitution for ∞/∞ http://www.cs.berkel[...] 2005-07-05
[15] 간행물 Handling floating-point exceptions in numeric programs 1996-03
[16] 웹사이트 Error Boundaries https://reactjs.org/[...] 2018-12-10
[17] 웹사이트 Vue.js API https://vuejs.org/v2[...] 2018-12-10
[18] 웹사이트 Error handling with Vue.js https://catchjs.com/[...] 2018-12-10
[19] 웹사이트 第 5 章 例外処理 (C++ プログラミングガイド) https://docs.oracle.[...] 2019-10-26
[20] 웹사이트 IPA ISEC セキュア・プログラミング講座:C/C++言語編 第6章 フェイルセーフ:体系だてたエラーハンドリング https://www.ipa.go.j[...] 2019-10-26
[21] 웹사이트 エラー処理をパターンにはめよう http://codezine.jp/a[...] 2019-10-26
[22] 웹사이트 Appendix E: Standard-Library Exception Safety in "The C++ Programming Language" (3rd Edition).Addison-Wesley, ISBN 0-201-88954-4 http://www.stroustru[...] 2013-05-01
[23] 웹사이트 Exception-Safety in Generic Components http://www.boost.org[...] 2013-05-01
[24] url http://www.open-std.[...]
[25] url "/EH (例外処理モデル)" https://msdn.microso[...]
[26] 문서
[27] 문서
[28] 문서
[29] url class StopIteration http://doc.ruby-lang[...]
[30] url 組み込み例外 http://docs.python.j[...]
[31] url module function Kernel.#throw http://doc.ruby-lang[...]
[32] 문서
[33] 문서
[34] url GetFileSize 関数 https://msdn.microso[...]
[35] url GetFileSize function (Windows) https://msdn.microso[...]
[36] 웹사이트 C言語規格のドラフト https://port70.net/~[...] 2018-11-21
[37] 웹인용 Interrupts https://people.cs.cl[...] 2022-01-04



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com